package com.ysstech.finance.thread;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.googlecode.aviator.AviatorEvaluator;
import com.ysstech.common.exception.BusinessException;
import com.ysstech.common.util.BigDecimalUtil;
import com.ysstech.common.util.DateUtil;
import com.ysstech.common.util.function.FunctionAdd;
import com.ysstech.finance.entity.*;
import com.ysstech.finance.enums.FinanceAuxEnum;
import com.ysstech.finance.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service
public abstract class VoucherGenerateService<T> implements AbsVoucherGenerate<T> {
    @Autowired
    private VoucherService voucherService;
    @Autowired
    private VoucherTemplateService voucherTemplateService;
    @Autowired
    private FinAccountService finAccountService;
    @Autowired
    private SubjectAdjustAuxilaryService subjectAdjustAuxilaryService;
    @Autowired
    private SubjectService subjectService;
    @Autowired
    private SubjectSubjectAuxilaryService subjectSubjectAuxilaryService;

    @Transactional(rollbackFor = Exception.class)
    public void doGenerate(Voucher voucher) throws Exception {
        // 获取基金信息
        QueryWrapper<Account> wrapper = new QueryWrapper<>();
        wrapper.eq("ID", voucher.getAccountId());
        Account account = finAccountService.getOne(wrapper);
        if (null == account || StringUtils.isBlank(account.getProjectId())) {
            throw new BusinessException("没有获取到基金对应的核算方案信息，请稍后重试！");
        }
        //删除凭证
        this.deleteVoucher(voucher);
        //凭证模板信息
        Map<String, List<VoucherTemplate>> map = getVoucherTemplate(account);
        if (CollectionUtils.isEmpty(map)) {
            throw new BusinessException("没有获取到凭证模板信息，请稍后重试！");
        }
        voucher.setAccount(account);
        // 生成凭证
        produceVoucher(map, voucher);
    }

    /**
     * 删除凭证
     *
     * @param voucher
     * @throws Exception
     */
    public void deleteVoucher(Voucher voucher) throws Exception {
        QueryWrapper<Voucher> wrapper = new QueryWrapper<>();
        if (null == voucher) {
            return;
        }
        wrapper.in("FTYPE", getVoucherFtype());
        wrapper.in("SOURCE", getVoucherSource());
        wrapper.eq("FDATE", voucher.getSelfdate());
        wrapper.eq("ACCOUNT_ID", voucher.getAccountId());
        voucherService.remove(wrapper);
    }


    /**
     * 获取凭证模板信息
     *
     * @return
     * @throws Exception
     */
    private Map<String, List<VoucherTemplate>> getVoucherTemplate(Account account) throws Exception {
        VoucherTemplate voucherTemplate = new VoucherTemplate();
        voucherTemplate.setListType(getVoucherFtype());
        voucherTemplate.setSource(getVoucherSource());
        voucherTemplate.setProjectId(account.getProjectId());
        List<VoucherTemplate> list = voucherTemplateService.queryVoucherTemplate(voucherTemplate);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Map<String, List<VoucherTemplate>> map = new HashMap<>();
        for (VoucherTemplate template : list) {
            if (null == template) {
                continue;
            }
            List<VoucherTemplate> listTemplate = null;
            String key = template.getSource() + "_" + template.getFtype();
            if (map.containsKey(key)) {
                listTemplate = map.get(key);
                if (CollectionUtils.isEmpty(listTemplate)) {
                    continue;
                }
                listTemplate.add(template);
                map.put(key, listTemplate);
            } else {
                listTemplate = new ArrayList<>();
                listTemplate.add(template);
                map.put(key, listTemplate);
            }
        }
        return map;
    }

    /**
     * 保存凭证
     *
     * @param map
     * @param voucher
     * @throws Exception
     */
    private void produceVoucher(Map<String, List<VoucherTemplate>> map, Voucher voucher) throws Exception {
        List<T> datas = queryDatas(voucher);
        Map<String, List<Object>> vouchersMap = getVoucherBean(datas);
        if (CollectionUtils.isEmpty(vouchersMap)) {
            throw new BusinessException("没有获取到对应的凭证信息，请稍后重试！");
        }
        for (Map.Entry<String, List<VoucherTemplate>> info : map.entrySet()) {
            List<VoucherTemplate> listTemplate = info.getValue();
            if (CollectionUtils.isEmpty(listTemplate)) {
                continue;
            }
            // 获取凭证数据集合
            List<Object> voucherList = vouchersMap.get(info.getKey());
            if (CollectionUtils.isEmpty(voucherList)) {
                continue;
            }
           /* VoucherSaveThread ht = new VoucherSaveThread(voucherList, listTemplate, voucher,
                    voucherService, subjectAuxilaryService);
            ThreadPool.getThreadPool().submit(ht);*/
            this.saveVoucher(voucherList, listTemplate, voucher);
        }
    }


    public void saveVoucher(List<Object> voucherList, List<VoucherTemplate> listTemplate, Voucher voucher) throws Exception {
        AviatorEvaluator.addFunction(new FunctionAdd());
        List<Voucher> addList = new ArrayList<>();
        log.info(DateUtil.dateToStrByFormat(new Date(), "yyyy-MM-dd HH:mm:ss:SSS") + voucher.getAccountId());
        //循环每一条数据，一条数据一个凭证
        for (Iterator<Object> iterator = voucherList.iterator(); iterator.hasNext(); ) {
            Object vo = iterator.next();
            if (null == vo) {
                continue;
            }
            Map<String, Object> mapData = getDataMap(vo); //获取一条数据的值
            String num = DateUtil.generateNumber();
            for (VoucherTemplate template : listTemplate) {
                if (null == template) {
                    continue;
                }
                voucher.setNum(num);
                if (2 == template.getIsLoop()) {
                    Voucher newVoucher = this.getVoucher(template, mapData, voucher);
                    if (null == newVoucher) {
                        continue;
                    }
                    addList.add(newVoucher);
                    continue;
                }
                if (1 == template.getIsLoop()) {
                   /* List<VoucherVo> list = vo.getListLoop();
                    for (VoucherVo voucherVo : list) {
                        Map<String, Object> mapDat2 = this.getDataMap(voucherVo);
                        Voucher newVoucher = this.getVoucher(template, mapDat2, voucher);
                        if (null == newVoucher) {
                            continue;
                        }
                        addList.add(newVoucher);
                    }*/
                    continue;
                }
            }
        }
        if (!CollectionUtils.isEmpty(addList)) {
            voucherService.saveBatch(addList);
        }
        AviatorEvaluator.removeFunction("ADD");
    }

    /**
     * 每条数据转成map
     *
     * @param vo
     * @return
     */
    private Map<String, Object> getDataMap(Object vo) throws Exception {
        Map<String, Object> map = new HashMap<>();
        Class<?> clz = vo.getClass();
        Method[] methods = clz.getMethods();
        for (Method method : methods) {
            if (null == method || !method.getName().startsWith("get")) {
                continue;
            }
            String key = method.getName().substring(3).toUpperCase();
            //通过反射的方法调用invoke获取属性值
            map.put(key, String.valueOf(method.invoke(vo)));
        }
        return map;
    }

    // 获取每个模板里表达式的值
    private Voucher getVoucher(VoucherTemplate template, Map<String, Object> mapData, Voucher bean) throws Exception {
        Voucher voucher = new Voucher();
        voucher.setNum(bean.getNum());
        voucher.setAccount(bean.getAccount());
        voucher.setId(DateUtil.generateUUID());
        voucher.setAccountId(bean.getAccount().getId());
        voucher.setFdate(DateUtil.strToLocalDate(bean.getSelfdate()));
        voucher.setSubjectId(template.getSubjectId());
        voucher.setDrandcr(template.getDrandcr());
        voucher.setSequence(template.getSequence());
        voucher.setFtype(template.getFtype());
        voucher.setRemark(template.getRemark());
        voucher.setAddTime(LocalDateTime.now());
        //获取金额和数量辅助项的表达式
        String moneyExp = template.getMoneyExpression();
        String amountExp = template.getAmountExpression();
        if (StringUtils.isNotBlank(moneyExp)) {
            Object objectMoney = AviatorEvaluator.execute(moneyExp.toUpperCase(), mapData);
            voucher.setMoney(null != objectMoney ? new BigDecimal(String.valueOf(objectMoney)) : BigDecimal.ZERO);
        }
        if (StringUtils.isNotBlank(amountExp)) {
            Object objectAmount = AviatorEvaluator.execute(amountExp.toUpperCase(), mapData);
            voucher.setAmount(null != objectAmount ? new BigDecimal(String.valueOf(objectAmount)) : BigDecimal.ZERO);
        }
        voucher.setPrice((null != voucher.getAmount() && voucher.getAmount().compareTo(BigDecimal.ZERO) != 0) ?
                BigDecimalUtil.divide(voucher.getMoney(), voucher.getAmount(), 4) : BigDecimal.ZERO);
        voucher.setCurrency(null != mapData.get("currency") ? String.valueOf(mapData.get("currency")) : "CNY");
        voucher.setFrate(null != mapData.get("frate") ?
                new BigDecimal(String.valueOf(mapData.get("frate"))) : new BigDecimal("1"));
        voucher.setSource(template.getSource());
        template.setProjectId(bean.getAccount().getProjectId());
        voucher.setSubjectCode(template.getSubjectCode());
        //证券代码
        Map<String, String> map = this.getAuxilary(template, mapData);
        if (CollectionUtils.isEmpty(map)) {
            return voucher;
        }
        String securitiesCode = map.get("SECURITIESCODE");
        if (StringUtils.isNotBlank(securitiesCode)) {
            voucher.setSecuritiesCode(securitiesCode);
            String SecuritiesName = this.getSecuritiesName(securitiesCode, FinanceAuxEnum.getEnum(map.get("CODE")));
            addSubjectCode(voucher, SecuritiesName);
        }
        return voucher;
    }

    /**
     * 自动增加明细科目
     *
     * @param voucher
     * @param securitiesName
     * @throws Exception
     */
    private void addSubjectCode(Voucher voucher, String securitiesName) throws Exception {
        if (StringUtils.isBlank(voucher.getSecuritiesCode())) {
            return;
        }
        // 证券代码的明细科目是否存在，不存在新增一个明细科目
        QueryWrapper<Subject> wrapper = new QueryWrapper<>();
        wrapper.eq("CODE", voucher.getSubjectCode() + "." + voucher.getSecuritiesCode());
        Subject subject = subjectService.getOne(wrapper);
        voucher.setSubjectId(subject.getId());
        if (null != subject) {
            return;
        }
        QueryWrapper<Subject> wrapper2 = new QueryWrapper<>();
        wrapper2.eq("ID", voucher.getSubjectId());
        Subject subject2 = subjectService.getOne(wrapper2);
        // 非明细科目
        if (null == subject2 || 1 == subject2.getDetailed()) {
            return;
        }
        Subject newSubject = new Subject();
        String id = DateUtil.generateUUID();
        voucher.setSubjectId(id);
        newSubject.setId(id);
        newSubject.setCode(voucher.getSubjectCode() + "." + voucher.getSecuritiesCode());
        newSubject.setName(securitiesName);
        newSubject.setParentId(subject2.getId());
        newSubject.setDrandcr(subject2.getDrandcr());
        newSubject.setAmount(subject2.getAmount());
        newSubject.setAssetsType(subject2.getAssetsType());
        newSubject.setProjectId(subject2.getProjectId());
        newSubject.setDetailed(1);
        subjectService.save(newSubject);
        //插入科目对应辅助项的信息
        QueryWrapper<SubjectSubjectAuxilary> wrapper3 = new QueryWrapper<>();
        wrapper3.eq("PROJECT_ID", subject2.getProjectId());
        wrapper3.eq("SUBJECT_ID", subject2.getId());
        List<SubjectSubjectAuxilary> list = subjectSubjectAuxilaryService.list(wrapper3);
        List<SubjectSubjectAuxilary> listadd = new ArrayList<>();
        for (SubjectSubjectAuxilary auxilary : list) {
            if (null == auxilary) {
                continue;
            }
            auxilary.setId(DateUtil.generateUUID());
            auxilary.setSubjectId(id);
            listadd.add(auxilary);
        }
        subjectSubjectAuxilaryService.saveBatch(listadd);
    }

    /**
     * 获取是否有明细到证券的辅助项科目，存入凭证表的证券代码类型
     *
     * @param template
     * @throws Exception
     */
    private Map<String, String> getAuxilary(VoucherTemplate template, Map<String, Object> mapData) throws Exception {
        // 没有核算项目的科目直接返回
        if (StringUtils.isBlank(template.getAdjustCode())) {
            return null;
        }
        // 加载科目对应的辅助项信息
        SubjectAdjustAuxilary subjectAdjustAuxilary = new SubjectAdjustAuxilary();
        subjectAdjustAuxilary.setProjectId(template.getProjectId());
        subjectAdjustAuxilary.setSubjectId(template.getSubjectId());
        subjectAdjustAuxilary.setAdjustCode(template.getAdjustCode());
        List<SubjectAdjustAuxilary> list = subjectAdjustAuxilaryService.queryAdjustAuxilaryBySubjectId(subjectAdjustAuxilary);
        if (CollectionUtils.isEmpty(list)) {
            return null;
        }
        Map<String, String> map = new HashMap<>();
        // 获取所有辅助项明细到证券的配置
        Map<String, String> stringMap = FinanceAuxEnum.getMapEnum();
        for (SubjectAdjustAuxilary auxilary : list) {
            if (null == auxilary) {
                continue;
            }
            if (1 != auxilary.getRequired()) { // 是否启用
                continue;
            }
            // 配置了明细到证券
            if (!CollectionUtils.isEmpty(stringMap) && stringMap.containsKey(auxilary.getAuxilaryCode())) {
                map.put("CODE", auxilary.getAuxilaryCode());
                map.put("SECURITIESCODE", String.valueOf(mapData.get(auxilary.getAuxilaryValue())));
                return map;
            }
        }
        return null;
    }

    /**
     * 从估值的基本信息表根据证券代码获取证券名称
     *
     * @param securitiesCode 证券代码
     * @param financeAuxEnum
     * @return
     * @throws Exception
     */
    private String getSecuritiesName(String securitiesCode, FinanceAuxEnum financeAuxEnum) throws Exception {
        switch (financeAuxEnum) {
            case STOCKCODE: //股票信息
                // 股票信息获取股票名称
                return "新增证券";
        }
        return null;
    }
}
